<?php

namespace Core\Data\UDD;

class Structure {

    private $arrDocuments = array();
    private $arrDatabases = array();

    function __construct () {

    }

    /**
     * Outputs an HTML document detailing the structure.
     *
     * @param string $strHTML Used to store the HTML instead of outputting it.
     */
    public function info ( &$strHTML = null ) {

        $objHTML = new \Core\Presentation\HTML\Document ();
        $objHTML->Title = "Core\Data\UDD\Structure::info()";

        $objHTML->addStyle( "*", "font-family", '"Helvetica", "Verdana"' );
        $objHTML->addStyle( "*", "color", "rgb(119,119,119)" );
        $objHTML->addStyle( "h1, h2", "color", "rgb(48,48,48)" );
        $objHTML->addStyle( "h4", "color", "white" );
        $objHTML->addStyle( "h4", "margin", "0 0 7px 0" );
        $objHTML->addStyle( "body", "background-color", "rgb(220,220,220)" );
        $objHTML->addStyle( "div[id=Container]", "margin", "0 auto 0 auto" );
        $objHTML->addStyle( "div[id=Container]", "width", "680px" );
        $objHTML->addStyle( "div[id=Container]", "text-align", "center" );
        $objHTML->addStyle( "th, td", "padding", "3px" );
        $objHTML->addStyle( "td", "text-align", "left" );
        $objHTML->addStyle( "div.table", "background-color", "rgb(174,174,174)" );
        $objHTML->addStyle( "div.table", "margin", "2em 0 1em 0" );
        $objHTML->addStyle( "div.table", "padding", "7px 0 3px 0" );
        $objHTML->addStyle( "div.table", "border", "1px solid rgb(160,160,160)" );
        $objHTML->addStyle( "table", "width", "100%" );
        $objHTML->addStyle( "table", "border-collapse", "collapse" );
        $objHTML->addStyle( "table", "border-top", "1px solid rgb(160,160,160)" );
        $objHTML->addStyle( "table", "border-bottom", "1px solid rgb(160,160,160)" );
        $objHTML->addStyle( "th, td, p, li", "font-size", "10pt" );
        $objHTML->addStyle( "span.Version", "font-size", "8pt" );
        $objHTML->addStyle( "th", "text-align", "left" );
        $objHTML->addStyle( "ul", "text-align", "left" );
        $objHTML->addStyle( "tr", "background-color", "rgb(213,213,213)" );
        $objHTML->addStyle( "tr.Odd", "background-color", "rgb(203,203,203)" );
        $objHTML->addStyle( "p.Status", "margin", "3px 0 0 0" );
        $objHTML->addStyle( "p.Status", "color", "white" );
        $objHTML->addStyle( "a", "text-decoration", "none" );
        $objHTML->addStyle( "a:hover", "text-decoration", "underline" );

        $strHTML = "";
        $strHTML .= '<div id="Container">';
        $strHTML .= "<h1>Core\Data\UDD\Structure::info()</h1>";

        $strHTML .= "<h2>Index</h2>";
        $strHTML .= "<ul>";
        $strHTML .= '<li><a href="#Documents">Documents</a></li>';
        $strHTML .= '<li><a href="#Databases">Databases</a><ul>';
        foreach ( $this->databases() as $objDatabase ) {
            $strHTML .= '<li><a href="#' . $objDatabase . '">' . $objDatabase . "</a><ul>";
            foreach ( $objDatabase->tables() as $objTable ) {
                $strHTML .= '<li><a href="#' . $objTable . '">' . $objTable . "</a></li>";
            }
            $strHTML .= "</ul></li>";
        }
        $strHTML .= "</ul></li>";
        $strHTML .= "</ul>";

        $strHTML .= '<a name="Documents"></a><h2>Documents</h2>';
        $strHTML .= "<table>";
        $strHTML .= "<tr><th>Filename</th><th>Project Name</th><th>Project Version</th><th>Revision</th></tr>";

        foreach ( $this->documents() as $objDocument ) {
            $strHTML .= "<tr>";
            $strHTML .= "<td>" . $objDocument->getFilename() . "</td>";
            $strHTML .= "<td>" . $objDocument->getProjectName() . "</td>";
            $strHTML .= "<td>" . $objDocument->getProjectVersion() . "</td>";
            $strHTML .= "<td>" . $objDocument->getRevision() . "</td>";
            $strHTML .= "</tr>";
        }

        $strHTML .= "</table>";
        $strHTML .= '<a name="Databases"></a><h2>Databases</h2>';

        foreach ( $this->databases() as $objDatabase ) {

            $strHTML .= '<a name="' . $objDatabase . '"></a>';
            $strHTML .= "<h3>Tables in " . $objDatabase . "</h3>";

            foreach ( $objDatabase->tables() as $objTable ) {

                $strHTML .= '<a name="' . $objTable . '"></a>';
                $strHTML .= '<div class="table">';
                $strHTML .= "<h4>" . $objTable . "</h4>";
                $strHTML .= "<table>";
                $strHTML .= "<tr><th>Field Name</th><th>Data Type</th><th>Key Type</th><th>Required</th><th>Size</th><th>Auto Increment</th></tr>";

                foreach ( $objTable->fields() as $objField ) {
                    $strHTML .= "<tr>";
                    $strHTML .= "<td>" . $objField . "</td>";
                    $strHTML .= "<td>" . $objField->DataType . "</td>";
                    $strHTML .= "<td>" . $objField->KeyType . "</td>";
                    $strRequired = ( $objField->Required ) ? "true" : "false";
                    $strHTML .= "<td>" . $strRequired . "</td>";
                    $strHTML .= "<td>" . $objField->Size . "</td>";
                    $strAutoIncrement = ( $objField->AutoIncrement ) ? "true" : "false";
                    $strHTML .= "<td>$strAutoIncrement</td>";
                    $strHTML .= "</tr>";
                }

                $strHTML .= "</table>";
                $strHTML .= '<p class="Status">';
                $strHTML .= count( (array) $objTable->fields() );
                $strHTML .= ( count( (array) $objTable->fields() > 1 ) ) ? " fields." : " field.";
                if ( !$objTable->getRecord() ) {
                    $strHTML .= " No record class.";
                }
                $strHTML .= "</p>";
                $strHTML .= "</div>";

            }

        }

        $strHTML .= "<p>" . \Core\NAME . '<br><span class="Version">' . \Core\version() . "</span></p>";
        $strHTML .= "</div>";

        $objHTML->importHTML( $strHTML );

        // Give every odd table row a differet color.
        $objHTML->find( "table tr:odd" )->setAttribute( "class", "Odd" );

        echo $objHTML->getHTML();

    }

    /**
     * Interprets a Universal Database Definition document.
     *
     * @param string $strFilename
     */
    public function interpretDocument ( $strFilename ) {
        // The actual interpreting of the document goes on here.
        // We assume that the document is valid, even though no schema is used yet.

        $xmlUDD = simplexml_load_file( $strFilename );

        $objDocument = $this->addDocument( $strFilename );
        $strVersion = $xmlUDD->attributes()->version; // Not used for anything yet.

        $objDocument->setProjectName( $xmlUDD->ProjectName[0] );
        $objDocument->setProjectVersion( $xmlUDD->ProjectVersion[0] );
        $objDocument->setRevision( $xmlUDD->Revision[0] );

        foreach ( $xmlUDD->Database as $xmlDatabase ) {

            $strDatabaseName = ( isset( $xmlDatabase->attributes()->name ) ) ? $xmlDatabase->attributes()->name : "__default";

            if ( in_array( $strDatabaseName, (array) $this->databases() ) ) {
                $objDatabase = $this->databases()->__default;
            } else {
                $objDatabase = $this->addDatabase( $strDatabaseName );
            }

            foreach ( $xmlDatabase->Table as $xmlTable ) {

                $strTableName = $xmlTable->attributes()->name;
                $strTablePrefix = ( isset( $xmlTable->attributes()->prefix ) ) ? $xmlTable->attributes()->prefix : null;

                // TODO: Throw an exception if the table already exists.

                $objTable = $objDatabase->addTable( $objDocument, $strTableName, $strTablePrefix );

                if ( isset( $xmlTable->attributes()->extends ) ) {
                    // TODO: Throw an exception if the table doesn't exist.
                    // NOTE: The parent table must already be added for this to work.
                    // NOTE: This requires parent tables to exist in the same database.
                    $strParent = $xmlTable->attributes()->extends;
                    $objTable->setParent( $objDatabase->tables()->$strParent );
                }

                // Add fields.
                foreach ( $xmlTable->Field as $xmlField ) {

                    $strName = $xmlField->attributes()->name;
                    $strDataType = ( isset( $xmlField->attributes()->datatype ) ) ? $xmlField->attributes()->datatype : TEXT;

                    $objField = $objTable->addField( $strName, $strDataType );

                    if ( isset( $xmlField->attributes()->key ) ) {
                        $objField->KeyType = $xmlField->attributes()->key;
                        if ( !isset( $xmlField->attributes()->autoincrement ) and $xmlField->attributes()->key == PRIMARY_KEY ) {
                            $objField->AutoIncrement = true;
                        }
                    }

                    if ( $xmlField->attributes()->required == "true" ) {
                        $objField->Required = true;
                    }

                    if ( isset( $xmlField->attributes()->size ) ) {
                        $objField->Size = (integer) $xmlField->attributes()->size;
                    }

                    if ( isset( $xmlField->attributes()->autoincrement ) ) {
                        $objField->AutoIncrement = ( $xmlField->attributes()->autoincrement == "true" ) ? true : false;
                    }

                }

                // Add record, if set.
                if ( $xmlTable->attributes()->norecord != "true" ) {
                    $objTable->addRecord();
                }

                // TODO: Walk through methods as well.

            }

        }

    }

    /**
     * Creates and appends a new Universal Database Definition document.
     *
     * @param string $strFilename
     */
    public function addDocument ( $strFilename ) {
        $objDocument = new Document ( $strFilename );
        $this->arrDocuments[] = $objDocument;
        return $objDocument;
    }

    /**
     * Creates and appends a new database.
     *
     * @param string $strName
     * @return Core_Data_UDD_Structure_Database
     */
    public function addDatabase ( $strName ) {
        $objDatabase = new Database ( $strName );
        $this->arrDatabases[(string) $objDatabase] = $objDatabase;
        return $objDatabase;
    }

    /**
     * Gets the databases.
     *
     * @return ArrayObject
     */
    public function databases () {
        return (object) $this->arrDatabases;
    }

    /**
     * Gets the documents.
     *
     * @return ArrayObject
     */
    public function documents () {
        return (object) $this->arrDocuments;
    }

    /**
     * Saves the structure into Universal Database Definition documents.
     */
    public function save () {
        // To be written later.
    }

}

?>
